SocketTimeout 后客户端怎么做和服务端怎么做
背景
希望通过一个极简,几乎是人人都可以上手验证的实验来触及到一些深度的内容,然后再看看是否会激发你进一步自己设计类似实验和验证过程等
关于这种简单类型的实验欢迎给我提意见:比如你会不会做;太难、太容易?能学到东西吗?效果如何?我要如何改进
安装JDK和MySQL
1 | yum install -y java-1.8.0-openjdk.x86_64 java-1.8.0-openjdk-devel.x86_64 podman-docker.noarch wireshark |
测试环境机器是99块一年购买的aliyun ECS,OS版本选ALinux3,对应内核版本:
1 | 5.10.134-15.al8.x86_64 |
测试使用的MySQL 版本:
1 | mysql> \s |
客户端
1 | 测试代码(复制粘贴就可以编译运行了,运行时需要下载jdbc mysql driver,链接见附录): |
服务端对应的抓包
如果OS 比较老,安装的tshark 也较老,那么命令参数略微不一样,主要是 col.Info 这个列,没有 _ws 前缀:
1 | #tshark -i eth0 port 3306 -d tcp.port==3306,mysql -nn -T fields -e frame.number -e frame.time_delta -e tcp.srcport -e tcp.dstport -e col.Info -e mysql.query |
如果是阿里云 99 买了ECS,安装的内核版本较高比如4.19,那么配套安装的tshark也较高,就用如下命令:
1 | #tshark -i eth0 -Y "tcp.port==3306" -d tcp.port==3306,mysql -T fields -e frame.number -e frame.time -e frame.time_delta -e tcp.srcport -e tcp.dstport -e tcp.len -e _ws.col.Info -e mysql.query |
如果你的 tshark 版本较高,以上命令行可以改为:
1 | tshark -i lo -Y "tcp.port==3306" -T fields -e frame.number -e frame.time_delta -e tcp.srcport -e tcp.dstport -e _ws.col.Info -e mysql.query |
GPT4.0 Turbo 对上面这个 tshark 命令的解释:
1 | 你提到的命令使用tshark捕获在端口3306(MySQL的默认端口)上的网络流量,并提取特定的字段进行显示。tshark是Wireshark的命令行版本,一个非常强大的网络协议分析工具。 |
最后用一张大截图来演示这个实验:
PreparedStatement 验证
测试代码,用Prepared执行三次查询:
1 | PreparedStatement stmt = conn.prepareStatement(sql); |
如图绿色是Prepared过程不会真执行 Select 查数据,只是把这条SQL 发给Server,让Server 提前编译,可以看出来编译时间0.000146秒(绿色方框),因为SQL 非常简单;三个红色线分别是3次真正的查询,都走了Prepared(不再传 Select了),不过时间很不稳定,所以这个统计必须大批量。红色方框是三次通过Prepared 执行 Select 查数据的执行时间:
结论
作为一个CRUD boy从以上实验中你可以学到哪些东西?
- 客户端报错堆栈要熟悉,Communications link failure (很多原因可以导致这个错误哈)和 java.net.SocketTimeoutException: Read timed out
- JDBC 连接参数要配置socketTimeout,不配置会导致很多很多故障,显得CRUD boy太业余
- 抓包,从抓包中学到每个动作,反过来分析原因,比如这次报错就是客户端发送了查询过1.7秒主动断开,所以问题在客户端,1.7秒也要敏感
- 最重要的是学到这个实验过程,比如再自己去试试分析 PreparedStatement 的工作原理,如何才能让 PreparedStatement 生效
进一步学习
你可以把抓包保存,然后下载到wireshark中,能看到具体每一个包的详细内容,比如加密后的密码、Prepared statement是个啥(一个唯一id)
比如明明MySQL Server感知到了连接断开错误(Message: Got an error reading communication packets) 还要挣扎着返回这个错误信息给客户端有必要吗?
java 跑着,直接kill -9 java-pid 看看服务端收到什么包?(有经验后下次看到这样的症状就知道为啥了)
参考
后续 Debug
为啥我的Java 代码跑半天也不报错:
jstack -p java-pid ,可以看到main 卡在执行SQL 后等结果的堆栈里,所以不是Java sleep了,等看对端MySQL 在干什么
1 | "Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f6a5c0db000 nid=0x109d8e in Object.wait() [0x00007f6a60a75000] |
抓包
为啥抓不到任何包?
先确认3306 端口是你的MySQL 在跑:
1 | # ss -lntp |grep 3306 |
:3306 中的‘“” 表示MySQLD 监听本机任何网卡的3306端口,查看一下网卡名字:
1 | # ip a |
尝试 tshark -i any – any是个什么鬼,展开学习下抓包参数
这里对select sleep 不确定的话可以Google sleep的单位、用法;也可以MySQL Client 验证一下
1 | # mysql -h127.1 -uroot -p123 |
终于能抓到包了
Kill Java
1 | 56 0.014671013 40102 3306 Request Prepare Statement select sleep(60), id from sbtest1 where id= ? |
mysql kill pid
1 | # tshark -i lo -Y "tcp.port==59636" -T fields -e frame.number -e frame.time_delta -e tcp.srcport -e tcp.dstport -e _ws.col.Info -e mysql.query |
kill mysqld pid
1 | ]# tcpdump -i lo port 50436 |
视频学习
如果你也想试试这个实验的话,可以参考我们的视频:https://meeting.tencent.com/user-center/shared-record-info?id=c0962ad4-16bc-4ac8-83ab-2e302c372e73&is-single=false&record_type=2&from=3
如果你觉得看完对你很有帮助可以通过如下方式找到我
find me on twitter: @plantegg
知识星球:https://t.zsxq.com/0cSFEUh2J
开了一个星球,在里面讲解一些案例、知识、学习方法,肯定没法让大家称为顶尖程序员(我自己都不是),只是希望用我的方法、知识、经验、案例作为你的垫脚石,帮助你快速、早日成为一个基本合格的程序员。
争取在星球内:
- 养成基本动手能力
- 拥有起码的分析推理能力–按我接触的程序员,大多都是没有逻辑的
- 知识上教会你几个关键的知识点